/**
 * 
 */

/**
 * @author Davide Dal Bosco
 * @author Francesco Cardin
 *
 */


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;


public final class Controller implements ActionListener {
	
	private class NullMove extends Exception {}

	private Model model;
	private GameModeView gameModeView;
	private View gameView;
	private int cutoffDepth;
	private int mode;
	
	class MuoviBtnListener implements ActionListener {
		
		@Override
		public void actionPerformed(ActionEvent e) {
			gameView.setEnabledMuovi(false);
			boolean win = false;
			try{
				if(mode==1){//Gigante vs AI
					win = giantMove(false);
					if(!win)
						win = dwarfsMove(true);
				}else if(mode==2){//Nani vs AI
					win = dwarfsMove(false);
					if(!win)
						win = giantMove(true);
				}else{//Gigante vs Nani
					win = false;
					if(Model.getTurno() == Personaggio.nano)
						win = dwarfsMove(false);
					else
						win = giantMove(false);
				}
			}catch (NullMove exc) {}
			
			if(!win) gameView.setEnabledMuovi(true);
		}
		
	}

	public Controller(Model model, GameModeView gameModeView, View gameView) {
		this.model = model;
		this.gameModeView = gameModeView;
		this.gameModeView.addBtnGiocaListener(this);
		this.gameView = gameView;
	}
	
	private boolean dwarfsMove(boolean aIMove) throws NullMove{
		boolean win = false;
		Mossa move = (!aIMove ? userMove(Personaggio.nano, model.getPosiz_nani(), model.getPosiz_gigante(),model.getAdiacenze(), model.getLivelli_grafo(), gameView.getIniPos(), gameView.getFinPos()) :
			new CacciaAlGiganteAI().scelta_mossa(model.getPosiz_nani(), model.getPosiz_gigante(), model.getAdiacenze(), model.getLivelli_grafo(), "nano", 0, cutoffDepth, model.getRepetitionSeq()));
		if(move != null){
			fai_mossa_nano(move, model.getPosiz_nani(), model.getPosiz_gigante());
			
			Shot shot = new Shot(model.getPosiz_gigante(), model.getPosiz_nani());
			model.pushShot(shot);
			
			gameView.repaintBoard(model.getPosiz_nani(), model.getPosiz_gigante());
			Model.changeTurno(Personaggio.gigante);
			if (vittoria_nani(model.getPosiz_nani(), model.getPosiz_gigante())){
		    	win = true;
		    	gameView.log("I nani hanno vinto!");//fine partita
			}
		}else
			throw new NullMove();
		return win;
	}
	
	private boolean giantMove(boolean aIMove) throws NullMove{
		boolean win = false;
		Mossa move = (!aIMove ? userMove(Personaggio.gigante, model.getPosiz_nani(), model.getPosiz_gigante(), model.getAdiacenze(), model.getLivelli_grafo(), model.getPosiz_gigante(), gameView.getFinPos()) :
		  new CacciaAlGiganteAI().scelta_mossa(model.getPosiz_nani(), model.getPosiz_gigante(), model.getAdiacenze(), model.getLivelli_grafo(), "gigante", 0, cutoffDepth, model.getRepetitionSeq()));
		if(move != null){
			model.setPosiz_gigante(fai_mossa_gigante(move,model.getPosiz_nani(), model.getPosiz_gigante()));
	        
			Shot shot = new Shot(model.getPosiz_gigante(), model.getPosiz_nani());
			model.pushShot(shot);

		    gameView.repaintBoard(model.getPosiz_nani(), model.getPosiz_gigante());
			Model.changeTurno(Personaggio.nano);
	    	if (vittoria_gigante(model.getPosiz_gigante()) 
	    			|| genera_mosse_nani(model.getPosiz_nani(), model.getPosiz_gigante(), model.getAdiacenze(), model.getLivelli_grafo()).size()==0){
	    		win = true;
		    	gameView.log("Il gigante ha vinto!");//fine partita
	    	}else if(model.giantWinForRepetition()){
	    		win = true;
		    	gameView.log("Il gigante ha vinto per ripetizione mosse!");//fine partita
	    	}
		}else
			throw new NullMove();
		return win;
	}
	
    //"ascoltatore" bottone "Gioca!"
	@Override
	public void actionPerformed(ActionEvent e) {
		mode = gameModeView.getMode(); 
		int giantInitPos = gameModeView.getGiantInitPos();
		
		if(gameModeView.getDifficulty().compareTo("facile")==0) cutoffDepth = 2;
		else if(gameModeView.getDifficulty().compareTo("media")==0) cutoffDepth = 6;
		else cutoffDepth = 10;

		gameModeView.dispose();//distrugge il frame di scelta della modalita'
		
		initGame(giantInitPos);

		gameView.repaintBoard(model.getPosiz_nani(),model.getPosiz_gigante());
		gameView.setVisible(true);
		if(mode==1){//Gigante vs AI
			gameView.setEnabledMuovi(false);
			try {
				dwarfsMove(true);
			} catch (NullMove e1) { }
			gameView.setEnabledMuovi(true);
		}
		gameView.addMuoviBtnListener(new MuoviBtnListener());
		
	}
	
	
	private void initGame(int giantInitPos) {
		
		// pendente: storia, rigiocare
		//inizializzazione
		model.prepara_matrice_di_gioco(model.getAdiacenze());
		model.prepara_livelli(model.getLivelli_grafo());
		
		model.setPosiz_gigante(giantInitPos-1);//! model considera posizioni 0-7 
		for(int i=0; i<3; i++) 
			model.getPosiz_nani()[i]=i;
		
		//inizializza storia
		Shot shot = new Shot(model.getPosiz_gigante(), model.getPosiz_nani());
		model.pushShot(shot);
		
	}
	
	public boolean vittoria_nani(int p_n[],int p_g) {   
		boolean vittoria = false;  
		if (p_g == 7)
			if ((p_n[0] == 4) || (p_n[0] == 5) || (p_n[0] == 6))
				if ((p_n[1] == 4) || (p_n[1] == 5) || (p_n[1] == 6))
					if ((p_n[2] == 4) || (p_n[2] == 5) || (p_n[2] == 6))
						vittoria = true;
		return vittoria;
	            
	}
	
	public boolean vittoria_gigante(int p_g) { 
		return p_g == 0;
		
	}
	
	
	Mossa userMove(Personaggio x, int p_n[], int p_g,int A[][],int l_g[], int currentPos, int nextPos) {  

        Mossa move = null;
        if (x == Personaggio.nano) {
	        if ((currentPos != p_n[0]) && (currentPos != p_n[1]) && (currentPos != p_n[2]))
	        	gameView.log("Attenzione! Non hai selezionato un nano!");
	        else{
	        	if (!errore_mossa(currentPos,nextPos,A,p_n,p_g)) {
	        		if (l_g[nextPos] >= l_g[currentPos]) {
	        			move = new Mossa();
	        			move.protagonista = x;
	                    move.q_nano = quale_nano(currentPos,p_n);
	                    move.pre = currentPos;
	                    move.post = nextPos;
	                }
	        		else gameView.log("Attenzione! I Nani non possono tornare indietro!");
	            }
	        }
	    }else{//mossa del gigante
	    	if (!errore_mossa(p_g,nextPos,A,p_n,p_g)) {
	    		move = new Mossa();
	        	move.protagonista = x;
	            move.post = nextPos;
	            move.pre = currentPos;
	        }
	    }
		return move;
	
	}

	public int quale_nano(int pcorrente,int p_nani[]) {
		
	  boolean trovato = false;
	  int qnano = 0;

	  for (int i = 0; i < 3 && !trovato; i++) {
		  if (p_nani[i] == pcorrente) {
			  qnano = i;
	          trovato = true;
	      }
	  }
	  return qnano;
	}
	
	public void fai_mossa_nano(Mossa K, int p_n[], int p_g) {

		p_n[K.q_nano] = K.post;
	}
	
	public int fai_mossa_gigante(Mossa K, int p_n[], int p_g) {
		
		return K.post;
	}
	
	
	public boolean errore_mossa(int pcorrente,int psucc, int A[][], int p_n[], int p_g) { 
		
		boolean errore = false;
	  
	    if (pcorrente == psucc) {
	    	errore = true;
	        gameView.log("Attenzione! Non si può rimanere fermi nello stesso nodo!");
	    }
	    else if ((psucc < 0) || (psucc > 7)) {
	    	errore = true;
	        gameView.log("Attenzione! Il nodo selezionato per lo spostamento non esiste!");
	    }
	  else if (A[pcorrente][psucc] != 1) {
	      errore = true;
	      gameView.log("Attenzione! Il nodo selezionato non è raggiungibile!");
	  }
	  else if (p_g == psucc) {
		  gameView.log("Attenzione! Il nodo "+(p_g+1)+" è gia occupato dal Gigante!");
	      errore = true;
	  }
	  else {
		  for (int i = 0;i < 3;i++)
			  if (p_n[i]==psucc) {
				  gameView.log("Attenzione! Il nodo "+(p_n[i]+1)+" è già occupato da un Nano!");
	              errore = true;
	           }
	  }
	    return errore;
 }
	
    public static Vector<Mossa> genera_mosse_gigante(int nani[], int gigante, int board[][], int livelli_grafo[]) {
    	Vector<Mossa> vettore = new Vector<Mossa>();
    	Personaggio gig = Personaggio.gigante;
    	for (int j = 0; j < 8; j++) {
    		if (board[gigante][j] == 1 && !occupato_nano(nani,j)) {
    			Mossa mossa = new Mossa();
    			mossa.protagonista = gig;
    			mossa.post = j;
    			vettore.add(mossa);
    		}
    	}
    	/*for (int i=0 ; i<vettore.size() ; i++){
    		  System.out.print("il gigante puo andare in ");
    		  System.out.println(vettore.get(i).post);
    	}*/
    	return vettore;
    }
    
    public static Vector<Mossa> genera_mosse_nani(int nani[], int gigante, int board[][], int livelli_grafo[]) {
    	Vector<Mossa> vettore = new Vector<Mossa>();
    	Personaggio nan = Personaggio.nano;
    	int posizione_nano_corrente = 0;
    	int nano_corrente = -1;
    	for (int i=0; i<3; i++) {
    		posizione_nano_corrente = nani[i];
    		nano_corrente = i;
    		//System.out.print(i);
    		for (int j=0; j<8; j++) {
    			if (board[posizione_nano_corrente][j] == 1 && !occupato_nano(nani,j) && !occupato_gigante(gigante,j) && !mossa_indietro(posizione_nano_corrente,livelli_grafo,j)) {
    				Mossa mossa = new Mossa();
    				mossa.protagonista = nan;
        			mossa.q_nano = nano_corrente;
        			mossa.post = j; 
        			vettore.add(mossa);
    			}
    		}
    	}
    	/*for (int i=0 ; i<vettore.size() ; i++){
    		  System.out.print("il nano ");
    		  System.out.print(vettore.get(i).q_nano);
    		  System.out.print(" puo spostarsi in ");
    		  System.out.println(vettore.get(i).post);
    	}*/
    	return vettore;
    }
    
    // metodo per recuperare le mosse del gigante in avanti (proibite mosse indietro e laterali)
    public static Vector<Mossa> genera_mosse_gigante_check_livello(int nani[], int gigante, int board[][], int livelli_grafo[]) {
    	
       	    Vector<Mossa> vettore = new Vector<Mossa> ();
    	    Personaggio gig = Personaggio.gigante;
    	
    	    for (int j = 0; j < 8; j++) {
    		
    		    if (board[gigante][j] == 1 && !occupato_nano(nani,j) && mossa_indietro(gigante,livelli_grafo,j) && !mossa_laterale(gigante,livelli_grafo,j)) {
    			
    			    Mossa mossa = new Mossa();
    			    mossa.protagonista = gig;
    			    mossa.post = j;
    			
    			    vettore.add(mossa);
    			
    			
          		  
          		    }
    			
    			
    		    }
    	
    	
    	    return vettore;
    		
    		
    	    }


    public static boolean occupato_nano(int nani[], int pos) {
    	for (int i=0; i<3; i++) {
    		if (nani[i] == pos)
    			return true;	
    	}
    	return false;
    }
    
    public static boolean occupato_gigante(int gigante, int pos) {
    	if (gigante == pos)
    		return true;
    	return false;
    }
    
    public static boolean mossa_indietro(int pos_nano, int livelli_grafo[], int j) {
    	if (livelli_grafo[pos_nano] > livelli_grafo[j])
    		return true;
    	return false;    	
    }
    
    // metodo per calcolare se la mossa passata come parametri è una mossa laterale
    public static boolean mossa_laterale(int pos_nano, int livelli_grafo[], int j) {
	
	    if (livelli_grafo[pos_nano] == livelli_grafo[j])
		    return true;
	
	    return false;
	
	
    }

    // metodo per calcolare se c'è un nano nella casella 1
    public static boolean nano_in_uno(int nani[]) {
	
	    for (int i = 0; i < 3; i++)
		    if (nani[i] == 0)
			    return true;
	
	    return false;
	
	
    }


}
